#!/usr/bin/env bash

# ywloader 命令补全脚本
# 
# 支持基本子命令的补全能力
#   add     添加项目到仓库索引
#   change  修改一条项目
#   del     删除一条项目
#   init    初始化当前目录为 Youwant 仓库
#   list    列出仓库项目索引
#   logo    打印可执行程序的 logo 头部
#   remote  使用远程仓库源
#   search  搜索仓库项目索引
#   serve   启动一个 web 服务(默认:8080)
#   show    显示项目详细信息
#   use     使用快速项目模板
#   version 打印版本信息


# ywloader 命令调试环境变量
# 如果此环境变量被设置将会打印命令在补全时的调试信息
_ywloader_debug() {
    # 使用 BASH_COMP_DEBUG_FILE 指定的文件进行输出调试信息
    #          v 如果在 bash 命令行中定义了这个环境变量，将传入本函数的所有内容输出(追加)到调试文件
    if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then
        echo "$@" >> "${BASH_COMP_DEBUG_FILE}"
    fi
}

# ywloader 命令调试文件清空函数
# 与 _ywloader_debug 不相同，且用于首次触发执行补全时清空调试文件
_ywloader_debug_clean() {
    if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then
        echo "" > "${BASH_COMP_DEBUG_FILE}"
    fi
}

# 简单的去除两边空白内容的 shell 函数
trim() {
    local str=$1
    #     v 要处理的字符串
    echo "${str}" | grep -o "[^ ]\+\( \+[^ ]\+\)*"
    #  -                             ^ 这是空格
    #  6                                      ^ 已经到结尾了
    #  5                                ^ 不为空的部分
    #  4                               ^ 加上
    #  3                            ^ 可能包含空格的部分
    #  2                          ^ 加上
    #  1                     ^ 不为空开头的部分
    # 与普通的 正则表达式(regexp)可能不一样，
    # grep 表示取出 '表达的内容' 为主要意思
    # 所以： 不为空开头的部分 加上可能包含空格的部分 再加上不为空的部分

    # 2: (1个指令,0个文件): RFC 1928 - SOCKS 5 协议中文文档「译」 
    #                    ^ 这个空格开头的不包含
    #                     ^R     这个是可能包含空格的部分    」^
    #                                                      ^ 已经结尾了呢，后面的就没有了
}

# 去除来自 补全过程中出现的 cur 词前面的 ' 与空白字符
# 所适用的场景情况
# $ ywloader show base-go
# $ ywloader show 'base-go
#                 ^ 将会去除这个内容
# 如果在这个情况下
trim_cur() {
    local str=$1
    echo "${str}" | grep -o "[^']\+\('\+[^']\+[^' ]\)*"
}

# ywloader 命令的子命令参数检查
#           v 补全时的当前词内容
# 在补全时的 -xxx 标志检查，默认未出现 -xxx 将不进行处理子命令参数
#          ^ 出现 - 内容的情况下，将
# words: 当前触发补全的命令行所有词
    # v主命令   v子命令
    # ywloader xxxx -
    #               ^被检查项
_ywloader_check_flags() {
    _ywloader_debug "_ywloader_check_flags words:" ${words[1]}
    _ywloader_debug "_ywloader_check_flags cur:" ${cur}

    # flags 用于存储对应子命令的参数标志列表
    local flags=()
    #             v 如果当前光标所在词(cur)是以 - 开头的
    if [[ $cur == -* ]]; then
        # words: 当前触发补全的命令行所有词列表，不同的下标可以取出不同的命令参数
        # ywloader 子命令  参数列表
        # ^ 0      ^ 1    ^ ......
        #    v 预进行筛选一些子命令
        case ${words[1]} in 
            # 默认 add/change 将拥有可接受的 -g -f 标志(那么其它命令也是相同逻辑)
            add|change)
                flags=( "-g" "-f" )
                ;;
            list|show)
                flags=( "-g" )
                ;;
            use)
                flags=( "-g"  "-a" )
                ;;
            serve)
                flags=( "-p" )
                ;;
            search)
                flags=( "-j" "--all")
                ;;
        esac
        
        # 打印一行调试信息，输出 flags 数量(如果未在筛选子命令内，flags 将是空的)
        #                                           v flags 数量 
        _ywloader_debug "_ywloader_check_flags:" "${#flags[@]} -ge  1"

        # 如果  v flags 数量 大于或等于 1 个(Greater than or equal to)
        # -ge: greater equal
        if [[ ${#flags[@]} -ge 1 ]]; then
            # 准备补全的内容列表，与当前词一起传入 compgen 进行自动补全
            _ywloader_debug "_ywloader_check_flags flags:" "${flags[@]}"

            # compgen：
            # 用法：compgen [-abcdefgjksuv] [-o 选项]  [-A 动作] [-G 全局模式] [-W 词语列表]  [-F 函数] [-C 命令] [-X 过滤模式] [-P 前缀] [-S 后缀] [词语]
            # 使用 -W 补全列表
            #   compgen -W "aaa abbb ccc" a  
                # aaa
                # abbb
                # 例如提供了  aaa abbb ccc 三个单词列表，并再提供一个用户输入的 a 单词
                # 将会输出匹配的开头包含 'a' 的单词列表
            COMPREPLY=( $( compgen -W "${flags[@]}" "$cur"))
            # 这里同理 与 check_global 部分的内容一样
            return 1
        fi
        # 如果未匹配到或筛选到任何子命令的 flags，将会进行最后的一次打印
        _ywloader_debug "_ywloader_check_flags flags:" "${flags[@]}"
    else
        # 如果还未到触发以 - 开头的逻辑，将打印此条信息 : not -*
        _ywloader_debug "_ywloader_check_flags flags: not -*" 
    fi
    return 0
}

# ywloader 命令的全局标志检查
# 在补全时的 -g 标志检查，默认未出现 -g 将不进行处理 global 变量值
# words: 当前触发补全的命令行所有词
    # v主命令   v子命令  v其它部分
    # ywloader xxxx -g xxxx
    #               ^被检查项
_youwant_check_global() {
    _ywloader_debug "_youwant_check_global: " "${words[@]}"
        #   v默认情况 -g 将在主命令/子命令之后才出现
    for (( i=2; i < ${#words[@]}; i++ )) ; do
        #           ^取 words 所有内容的长度
        #      v如果words词组中现 v 这个内容
        if [[ "${words[i]}" == "-g" ]]; then
            #     v 并且当前 global 并未设置
            if [[ "${global}" == "0" ]]; then
                # 将设置全局 global 的值标记为 1(已标记为 global)
                global=1
            fi
            # v 退出函数/返回这个函数调用的结果，一般可用于:
            # 例如: 前置命令 || 后置命令
            #      '前置命令' 返回1的话，将会执行 '后置命令'
            return 1
        fi
    done
    return 0
}

# ywloader 命令的核心补全功能，只有在需要进行补全的命令中才会使用
# 此函数由 _ywloader_ 开头的各种子命令补全使用
# 
_youwant_completion_old() {
    # 由于最初是一次编写，不清楚到底有多少地方进行了各种逻辑补全与是否提供了 global 变量
    # 所以在这里进行了一个定义，表示在这里使用的变量，由 check_global 进行填充此 global 变量 
    local global=0 
    
    # 打印了两行 _youwant_completion 开头的调试信息
    # 其中有一行是 check_global 打印的
    _ywloader_debug "_youwant_completion: " "${words[@]}"
    _youwant_check_global
    _ywloader_debug "_youwant_completion global: " "${global}"

    # 判断在检查过的命令行参数中是否出现了 -g 内容，并由 global 记录为 1
    #      v 如果 global 为 0 表示未出现 -g 内容
    #      v 否则 global 为 1 表示已出现 -g 内容
    if [[ "${global}" == "0" ]]; then

        # ywloader list 将输出以下格式信息
        # 102: (0个指令,3个文件): cmake-c-regexp 
        # 103: (0个指令,3个文件): cmake-c-wavplay-asound 
        #      ^ 第二段          ^ 第三段
        # ^ 第一段
        for i in `ywloader list | cut -d':' -f3`; do 
            #    ^  对输出的信息 用 : 分割并取第 3 段的内容
            i="$(trim "$i")"
            #    ^ 使用 trim 函数清洗这个分割后的内容(两边的空格)
            # _ywloader_debug "completion for:" "$i"
            # if [[ "${#ITEMS[@]}" == "0" && "${ITEMS[@]}" == "" ]]
            
            # 追加到 ITEMS 变量
            ITEMS=(${ITEMS[@]} "$i")
            #      ^原始的内容   ^新的内容
            # fi
        done
    else
        # ywloader list -g 将输出全局项目内容，当然以下逻辑同上
        for i in `ywloader list -g | cut -d':' -f3`; do 
            i="$(trim "$i")"
            # _ywloader_debug "completion for:" "$i"
            # if [[ "${#ITEMS[@]}" == "0" && "${ITEMS[@]}" == "" ]]
            ITEMS=(${ITEMS[*]} "$i")
            # fi
        done
    fi
    # ^ 以上内容在拥有 120 项以上条目的时候，所处理的时间为1s以上
    # real    0m1.249s      这可是还未传入 compgen 进行补全，并且还未出现在屏幕上
    # 准备将其进行使用程序提供的 search 命令进行替换

    # 使用 ywloader search 处理
    # 以上用 ywloader list 与其它命令组合使用的搜索逻辑将被废除...

    # _ywloader_debug "trim_cur:" "if [[ \"${cur}\" == '* ]]"
    # if [[ "${cur}" == \'* ]]; then
    #     cur=`trim_cur "${cur}"`
    # fi
    # _ywloader_debug "trim_cur:" "${cur}"

    COMPREPLY=( $( compgen -W "${ITEMS[*]}" "$cur" ) )
    for (( i=0; i < ${#COMPREPLY[@]}; i++ ))
    do
        COMPREPLY[$i]="'${COMPREPLY[i]}'"
    done

    return 0
}

# ywloader 命令的核心补全功能，只有在需要进行补全的命令中才会使用
# 此函数由 _ywloader_ 开头的各种子命令补全使用
# 
_youwant_completion() {
    # 由于最初是一次编写，不清楚到底有多少地方进行了各种逻辑补全与是否提供了 global 变量
    # 所以在这里进行了一个定义，表示在这里使用的变量，由 check_global 进行填充此 global 变量 
    local global=0 
    
    # 打印了两行 _youwant_completion 开头的调试信息
    # 其中有一行是 check_global 打印的
    _ywloader_debug "_youwant_completion: " "${words[@]}"
    _youwant_check_global
    _ywloader_debug "_youwant_completion global: " "${global}"


    # 使用 ywloader search 处理全新匹配逻辑
    # 用 ywloader list 与其它命令组合使用的搜索逻辑被废除
    
    # 但有一些小小的东西 需要进行处理，因为某些地方与旧的补全不同
    # 例如在使用 trim_cur 来移除开头的 ' 字符的时候
        # 使用它的时机，应该在提供给 search 搜索之前进行处理
        # 一种是移除开头的 ' 另一种还可以是给它加上尾部的 '

    _ywloader_debug "trim_cur:" "if [[ \"${cur}\" == '* ]]"
    if [[ "${cur}" == \'* ]]; then
        cur=`trim_cur "${cur}"`
    fi
    _ywloader_debug "trim_cur:" "${cur}"


    # 判断在检查过的命令行参数中是否出现了 -g 内容，并由 global 记录为 1
    #      v 如果 global 为 0 表示未出现 -g 内容
    #      v 否则 global 为 1 表示已出现 -g 内容
    if [[ "${global}" == "0" ]]; then
        #  ^ 如果在补全时，在补全的命令中未出现全局标志
        
        if [[ "${cur}" != "" ]]; then
            #  ^ 如果当前光标所在部分不是空白，将尝试从 search 后补全当前词的尾部内容
            ITEMS=`ywloader search $cur`
        else
            # 如果是空白词将尝试补全所有内容，好像没啥必要，所以我为此增加了 --all 标志 
            ITEMS=`ywloader search --all`
        fi
    else
        # 如果  global 被设置为 1， 以下内容基本相同，只是多了一个 -g 的搜索参数标志
        if [[ "${cur}" != "" ]]; then 
            ITEMS=`ywloader search -g $cur`
        else
            ITEMS=`ywloader search -g --all`
        fi
    fi

    # 将打印一些调试信息，这是所有的将要进行提供的补全内容，并由 compgen 进行筛选
    _ywloader_debug " ----------- 以下是 search 的结果 -----------"
    for item in ${ITEMS[*]}; do
        _ywloader_debug "->" "${item}"
    done
    _ywloader_debug " ----------- 以上是 search 的结果 -----------"


    COMPREPLY=( $( compgen -W "${ITEMS[*]}" "$cur" ) )
    for (( i=0; i < ${#COMPREPLY[@]}; i++ ))
    do
        COMPREPLY[$i]="'${COMPREPLY[i]}'"
    done

    return 0
}

_ywloader_version() {
    COMPREPLY=()
}

_ywloader_init() {
    COMPREPLY=()
}

# ywloader add 命令的补全逻辑
_ywloader_add() {
    COMPREPLY=()
    _ywloader_check_flags || return

    # 
    _filedir
}

_ywloader_list() {
    COMPREPLY=()

    local global=0
    local ITEMS=()
    local IFS=$'\n'
    
    _ywloader_debug "_ywloader_list: " "$cur"
    _ywloader_check_flags || return 0
    _youwant_check_global
}

# ywloader show 命令的补全逻辑
_ywloader_show() {
    COMPREPLY=()
    # ^ 预清空待补全的逻辑

    # v 默认准备由 check_global 使用的存储补全词列表的变量
    local global=0  
    local ITEMS=()
    local IFS=$'\n'
    
    # 1.打印一条简单的调试信息，与当前光标所在词
    # 2.简单的进行子命令的 flags 参数标志检查，如果已经进行了补全 flags 将不进行处理后续
        # v 退出函数/返回这个函数调用的结果，一般可用于:
        # 前置命令 || 后置命令
        # 如果 '前置命令' 返回1的话，将会执行 '后置命令'
    # 3.检查命令单词列表中是否出现了全局标志，用于在实际的核心补全中使用(global)
    # 4.进行实际的补全动作
    _ywloader_debug "_ywloader_show: " "$cur"
    _ywloader_check_flags || return 0
    _youwant_check_global
    _youwant_completion
    # ^ 只有
}

_ywloader_use() {
    COMPREPLY=()

    local global=0
    local ITEMS=()
    local IFS=$'\n'
    
    _ywloader_debug "_ywloader_use: " "$cur"
    _ywloader_check_flags || return 0
    _youwant_check_global
    _youwant_completion
}

_ywloader_change() {
    COMPREPLY=()

    local global=0
    local ITEMS=()
    local IFS=$'\n'
    
    _ywloader_debug "_ywloader_change: " "$cur"
    _ywloader_check_flags || return 0
    _youwant_check_global

    # 处理组织参数，匹配结构为:
    #   ywloader change [-g] <label> [file ...]
    if [[ $cword -lt 3 ]];then
        _youwant_completion
    elif [[ "-g" == "${words[2]}" && $cword -eq 3 ]];then
        _youwant_completion
    else
        _filedir
    fi
}

_ywloader_del() {
    COMPREPLY=()

    local global=0
    local ITEMS=()
    local IFS=$'\n'
    
    _ywloader_debug "_ywloader_del: " "$cur"
    _ywloader_check_flags || return 0
    _youwant_check_global
    _youwant_completion
}

_ywloader_serve() {
    COMPREPLY=()
    
    _ywloader_debug "_ywloader_serve:" "${@}" "$curword"

    # 检查是否应该补全 flags 
    _ywloader_check_flags || return 0

    # 如果没有任何 flags 的话, 例如 -p <port>，直接就应该提示目录补全
    if [[ "${prev}" != "-p" ]]; then
        _filedir -d
        return
    fi
}

_ywloader_search() {
    COMPREPLY=()
    _ywloader_debug "_ywloader_search:" "${@}" "$curword"

    # 检查是否应该补全 flags 
    _ywloader_check_flags || return 0

    # 如果没有任何 flags 的话, 例如，直接就应该提示项目补全
    # ywloader search
    # 但绝对不是以下内容 
    # if [[ "${prev}" != "-p" ]]; then
    #     _filedir -d
    #     return
    # fi
}

_ywloader_help() {
    local curword=$1; shift
    _ywloader_debug "_ywloader_help:" "${@}" "$curword"
    COMPREPLY=($(compgen -W "${@}" "$curword"))
}

_ywloader() {
    # 调试操作: 清空调试文件内容
    _ywloader_debug_clean
    _ywloader_debug "========= starting completion logic =========="

# 1. 准备一些基本的信息，这些都是最基本的，也是在补全脚本中仅能获取的有限内容

    # 定义一些函数内变量，由 completion 驱动内置进行初始化内容
        # cur   当前的位置
        # prev  上一个词
        # words 所有词
        # cword 当前词
    local cur prev words cword
    _init_completion || return

    # 为了更好的进行识别
        # 将第一个词标记为可执行程序名称
        # 将第二个词标记为子命令
    local binary="${words[0]}"
    local command="${words[1]}"

    _ywloader_debug "========== _ywloader function env ============"
    _ywloader_debug "_ywloader cur:   " "${cur}"
    _ywloader_debug "_ywloader prev:  " "${prev}"
    _ywloader_debug "_ywloader words: " "${words[*]}"
    _ywloader_debug "_ywloader cword: " "${cword}"
    _ywloader_debug
    _ywloader_debug "_ywloader binary: " "${binary}"
    _ywloader_debug "_ywloader command:" "${command}"
    _ywloader_debug "========== _ywloader function env ============"

# 2. 准备此命令有关的次级信息

    # 子命令集合，如果子命令不在此中，将认为尚未完成子命令的提示，可直接使用此处进行补全
    local ywloader_help_commands=(
        version init add list show use del change serve search
    )

    local ywloader_commands=(
        help 
        ${ywloader_help_commands[*]}
    )

    COMPREPLY=()

    # 
    if [[ ${cword} == 1 ]]; then
        _ywloader_debug "_ywloader cword: ${cword} == 1"
        if [[ "${binary}" == "ywloader" ]]; then
            _ywloader_debug "_ywloader ywloader_commands[*]:" "${ywloader_commands[*]}"
            _ywloader_debug "_ywloader COMP_WORDS[1]:" "${COMP_WORDS[1]}"
            COMPREPLY=($(compgen -W "${ywloader_commands[*]}" "${COMP_WORDS[1]}"))
        fi
    elif [[ ${cword} -gt 1 ]]; then 
        _ywloader_debug "_ywloader cword: ${cword} -gt 1"
        case $command in 
            help)
                _ywloader_${command} "${cur}" "${ywloader_help_commands[*]}"
                ;;
            version|init|add|list|show|use|del|change|serve|search)
                _ywloader_${command}
                ;;
            *)
                ;;
        esac
    fi
}

# 在 Bash 中，输入 ywloader 时，补全操作将尝试调用 _ywloader 函数尝试生成补全内容。
    # _ywloader 函数将被输入三个参数：
        # 要补全的命令名、当前光标所在的词、当前光标所在的词的前一个词
        # COMP_WORDS
        # COMP_CWORD
        # COMP_LINE
complete -F _ywloader ywloader







# compgen：
# 用法：compgen [-abcdefgjksuv] [-o 选项]  [-A 动作] [-G 全局模式] [-W 词语列表]  [-F 函数] [-C 命令] [-X 过滤模式] [-P 前缀] [-S 后缀] [词语]
